#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "include/params.h"
#include "include/types.h"
#include "include/operat.h"
#include "include/virtware.h"
#include "include/module.h"
#include "include/libcore.h"
#include "include/vmerr.h"

static void * devctl_thread (void * arg);
status devctl_init (struct devctl * dctl, struct environ * env);
status devctl_csrrw_sync (struct devctl* dctl, csraddr_t addr, word send, word * recv, enum rwmod rw);
status devctl_raise_excep_async (struct devctl * dctl, word cause);
status devctl_add_module (struct devctl * dev, struct module * mod);
status devctl_remove_module(struct devctl * dev, modid_t modid);
status dev_ctl_start (struct devctl * dctl);
status dev_ctl_pause (struct devctl * dctl);
status dev_ctl_resume (struct devctl * dctl);
status dev_wait_until_stop (struct devctl * dctl);
status dev_ctl_stop (struct devctl * dctl);
status devctl_ctrl(struct devctl * dctl, enum dev_ctl_arg arg);

static void *
devctl_thread (void * arg) {
    status stat = STAT_SUCCESS;
    struct devctl * self, *dev;
    self = (struct devctl *) arg;
    dev = self;
    self->stat.running = true;
    struct dev_csrrw_req csrreq_buf;
    struct intrp_desc intdesc_buf;
    bool pausesig;
    bool stopsig;
    int csrop_suc, introp_suc;
    have_brk:
    usleep (DEVPOLL_TIME);
    while (true) {
        mlk_lock (self->stat.mlk);
        stopsig = self->stat.stopsig;
        pausesig = self->stat.pausesig;
        mlk_unlock (self->stat.mlk);
        if (stopsig) break;
        if (pausesig) {
            self->stat.paused = true;
            mlk_lock (self->stat.blk);
        }
        plk_lock (self->csrrw_ring.plk, PLK_DEVCTL);
        csrop_suc = ring_pophd (self->csrrw_ring, csrreq_buf);
        plk_unlock (self->csrrw_ring.plk, PLK_DEVCTL);
        // if (!csrop_suc) goto have_brk; 
        stat = STAT_SUCCESS;
        if (csrop_suc) {
            // struct req_stat rstat = REQS_INIT;
            exec_module_func (dev, csrrw, {
                .env = csrreq_buf.env,
                //.ctx = csrreq_buf.ctx,
                .addr = csrreq_buf.addr,
                .send = csrreq_buf.send,
                .recv = csrreq_buf.recv,
                .rw = csrreq_buf.rw,
                .rstat = csrreq_buf.rstat
            });
        }
        if (stat)
        DBG_LOG (devctl_thread, "Ops... error occurred while executing devcsrrw fn");
        // get_intr:
        mlk_lock (self->intrp_ring.mlk);
        introp_suc = ring_pophd (self->intrp_ring, intdesc_buf);
        mlk_unlock(self->intrp_ring.mlk);
        stat = STAT_SUCCESS;
        if (introp_suc) {
            // TODO : CPU中断环可能满了
            cpu_raise_excep_async (&self->env->cpu, intdesc_buf.cause);
            // goto get_intr;
        }
        if (stat)
        DBG_LOG (devctl_thread, "Ops... error occurred while raising intrp");
        if (!csrop_suc && !introp_suc)
            goto have_brk;
    }
    DBG_LOG (devctl_thread, "devctl thread exiting...");
    self->stat.running = false;
    pthread_exit (NULL);
};

status
devctl_init (struct devctl * dctl, struct environ * env) {
    status stat;
    stat = STAT_SUCCESS;
    bzero (dctl, sizeof (struct devctl));
    dctl->env = env;
    dctl->magic = BISCUIT_MAGIC;
    if (pthread_attr_init (&dctl->thread_info.attr)) {
        stat |= ERR_THREAD;
        DBG_LOG (devctl_init, "Unable to init thread attr");
        goto error;
    }
    plk_init (dctl->csrrw_ring.plk);
    if (mlk_init(dctl->thread_info.mlk))
    if (mlk_init(dctl->ctrl_mlk))
    if (mlk_init(dctl->stat.blk))
    if (mlk_init(dctl->stat.mlk))
    if (mlk_init(dctl->intrp_ring.mlk)) {
        stat |= ERR_THREAD;
        DBG_LOG (devctl_init, "Unable to init mutex");
        goto error;
    }
    mlk_lock (dctl->stat.blk);
    return stat;
    error:
    return stat;
}

status
devctl_csrrw_sync (struct devctl* dctl, csraddr_t addr, word send, word * recv, enum rwmod rw) {
    status stat = STAT_SUCCESS;
    struct req_stat rstat = REQS_INIT;
    struct devctl* dev = dctl;
    exec_module_func (dev, csrrw, {
        .env = dctl->env,
        .addr = addr,
        .send = send,
        .recv = recv,
        .rw = rw,
        .rstat = &rstat
    });
    return stat;
}

status
devctl_raise_excep_async (struct devctl * dctl, word cause) {
    status stat = STAT_SUCCESS;
    struct intrp_desc intdesc_buf = {
        .source = INTR_SRC_ASYNC,
        .cause = cause,
    };
    int op_suc;
    mlk_lock (dctl->intrp_ring.mlk);
    op_suc = ring_pushtl (dctl->intrp_ring, intdesc_buf);
    mlk_unlock (dctl->intrp_ring.mlk);
    if (!op_suc)
        stat |= ERR_FULLRING;
    return stat;
}

status
devctl_add_module (struct devctl * dev, struct module * mod) {
    status stat = STAT_SUCCESS;
    count_t i;
    for (i = 0; i < mod->items.item_n; i++) {
        if (mod->items.item_list[i].type == MI_DEV_CSRRW) 
            add_module_item (dev, csrrw, mod->items.item_list[i]);
    }
    if (stat & ERR_NRITEM) {
        //fprintf (stderr, "devctl_add_module: Unable to add module, MAX_MOD_ITEMS reached.\n");
        DBG_LOG (devctl_add_module,"removing mod[%llu]::csr", mod->modid);
        remove_module_item (dev, csrrw,mod->modid);
        DBG_LOG (devctl_add_module,"mod[%llu]::csr removed", mod->modid);
    }
    return stat;
}

status
devctl_remove_module(struct devctl * dev, modid_t modid) {
    status stat = STAT_SUCCESS;
    bool mod_exist;
    mod_exist = false;
    count_t i;
    for (i = 0; i < MAX_MOD_ITEM; i++) {
        if (dev->mods.csrrw_modid[i] == modid){
            mod_exist = true;
            break;
        }
    }
    if (mod_exist == false) {
        stat |= ERR_MNOINST;
        goto error;
    }
    remove_module_item (dev, csrrw, modid);
    return stat;
    error:
    return stat;
}


status
dev_ctl_start (struct devctl * dctl) {
    status stat;
    stat = STAT_SUCCESS;
    if (dctl->stat.running) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_start, "devctl is running.");
        goto error;
    }
    dctl->stat.stopsig = 0;
    if (pthread_create (&dctl->thread_info.id, &dctl->thread_info.attr, 
        devctl_thread, (void*)dctl)) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_start, "Unable to create thread");
        goto error;
    }
    return stat;
    error:
    return stat;
}

status
dev_ctl_pause (struct devctl * dctl) {
    status stat;
    stat = STAT_SUCCESS;
    if (!dctl->stat.running) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_pause, "devctl not running.");
        goto error;
    }
    mlk_lock (dctl->stat.mlk);
    dctl->stat.pausesig = true;
    mlk_unlock (dctl->stat.mlk);
    return stat;
    error:
    return stat;
}

status
dev_ctl_resume (struct devctl * dctl) {
    status stat = STAT_SUCCESS;
    if (!dctl->stat.paused) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_pause, "devctl not paused.");
        goto error;
    }
    mlk_lock (dctl->stat.mlk);
    dctl->stat.pausesig = false;
    mlk_unlock (dctl->stat.mlk);
    mlk_unlock (dctl->stat.blk);
    return stat;
    error:
    return stat;
}

status
dev_wait_until_stop (struct devctl * dctl) {
    status stat = STAT_SUCCESS;
    void * retval = NULL;
    if (!dctl->stat.running) 
        DBG_LOG (dev_wait_until_stop, "DEVCTL already stopped.");
    while (dctl->stat.running);
    if (pthread_join (dctl->thread_info.id, &retval)) {
        stat |= ERR_THREAD;
        goto error;
    }
    return stat;
    error:
    return stat;
}

status
dev_ctl_stop (struct devctl * dctl) {
    status stat = STAT_SUCCESS;
    if (!dctl->stat.running) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_stop, "devctl not running.");
        goto error;
    }
    if (dctl->stat.paused) {
        stat |= ERR_STATUS;
        DBG_LOG (dev_ctl_stop, "devctl is paused.");
        goto error;
    }
    mlk_lock (dctl->stat.mlk);
    dctl->stat.stopsig = true;
    mlk_unlock (dctl->stat.mlk);
    if (pthread_self () == dctl->thread_info.id) {
        DBG_LOG (dev_ctl_stop, "DEVCTL self");
        goto success;
    }
    if (dev_wait_until_stop(dctl)) {
        DBG_LOG (dev_ctl_stop, "Unable to stop CPU.");
        stat |= ERR_CONTROL;
        goto error;
    }
    //printf ("DEVCTL STOPPED.\n");
    success:
    return stat;
    error:
    return stat;
}


status
devctl_ctrl(struct devctl * dctl, enum dev_ctl_arg arg){
    status stat = STAT_SUCCESS;
    //status fnstat;
    mlk_lock (dctl->ctrl_mlk);
    switch (arg)
    {
        case DEV_CTL_START : {
            stat = dev_ctl_start (dctl);
            if (stat)
            DBG_LOG (devctl_ctrl, "Unable to start devctl");
            break;
        }
        case DEV_CTL_STOP : {
            stat = dev_ctl_stop (dctl);
            if (stat)
            DBG_LOG (devctl_ctrl, "Unable to stop devctl");
            break;
        }
        case DEV_CTL_PAUSE : {
            stat = dev_ctl_pause (dctl);
            if (stat)
            DBG_LOG (devctl_ctrl, "Unable to pause devctl");
            break;
        }
        case DEV_CTL_RESUME : {
            stat = dev_ctl_resume (dctl);
            if (stat)
            DBG_LOG (devctl_ctrl, "Unable to resume devctl");
            break;
        }
        default : {
            DBG_LOG (devctl_ctrl, "Invailed argument.");
            break;
        }
    }
    mlk_unlock (dctl->ctrl_mlk);
    return stat;
}
    